#pragma once
#include <assert.h>
#include "reverseiterator.hpp"

namespace Myvector
{

    template <class T>

    class vector

    {

    public:
        typedef T *iterator;
        typedef const T *const_iterator;
          // reverse iterator
        typedef ReIterator::reverse_iterator<iterator, T &, T *> reverse_iterator;
        typedef ReIterator::reverse_iterator<const_iterator, const T &, const T *> const_reverse_iterator;


        iterator begin()
        {
            return _start;
        }

        iterator end()
        {
            return _finish;
        }

        const_iterator cbegin() const
        {
            return _start;
        }

        const_iterator cend() const
        {
            return _finish;
        }

        const_iterator begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }

      
        reverse_iterator rbegin()
        {
            return end();
        }

        reverse_iterator rend()
        {
            return begin();
        }

        const_reverse_iterator crbegin() const
        {
            return cend();
        }

        const_reverse_iterator crend() const
        {
            return cbegin();
        }

        const_reverse_iterator rbegin() const
        {
            return cend();
        }

        const_reverse_iterator rend() const
        {
            return cbegin();
        }
        // construct and destroy

        vector()
        {
        }

        vector(int n, const T &value = T())
        {
            resize(n, value);
        }

        template <class InputIterator>

        vector(InputIterator first, InputIterator last)
        {
            reserve(last - first);
            while (first < last)
            {
                *_finish++ = *first++;
            }
        }

        vector(const vector<T> &v)
        {
            reserve(v.capacity());
            for (const_iterator it = v.cbegin(); it < v.cend(); it++)
            {
                push_back(*it);
            }
            // for (auto e : v)
            //{
            //     push_back(e);
            // }

            // memcpy(_start, v.cbegin(), v.capacity() * sizeof(T));
            //_finish = v.cend() - v.cbegin() + _start;
        }

        vector<T> &operator=(vector<T> &v)
        {
            vector<T> tmp(v);
            swap(tmp);
            return *this;
        }

        ~vector()
        {
            delete[] _start;
            _start = _finish = _endOfStorage = nullptr;
        }

        // capacity

        size_t size() const
        {
            return _finish - _start;
        }

        size_t capacity() const
        {
            return _endOfStorage - _start;
        }

        void reserve(size_t n)
        {
            assert(n >= 0);

            if (n > capacity())
            {
                T *tmp = new T[n];
                // memcpy(tmp, _start, size() * sizeof(T));

                size_t old_size = size();
                for (int i = 0; i < old_size; i++)
                {
                    tmp[i] = _start[i];
                }
                delete[] _start;

                _start = tmp;
                _finish = _start + old_size;
                _endOfStorage = _start + n;
            }
        }

        void resize(size_t n, const T &value = T())
        {
            assert(n >= 0);
            if (n > capacity())
            {
                reserve(n);
                while (_finish < _endOfStorage)
                {
                    *_finish = value;
                    _finish++;
                }
            }
            else if (n < capacity())
            {
                _finish = _start + n;
            }
        }

        ///////////////access///////////////////////////////

        T &operator[](size_t pos)
        {
            assert(pos < size());
            return *(_start + pos);
        }

        const T &operator[](size_t pos) const
        {
            assert(pos < size());

            return *(_start + pos);
        }

        ///////////////modify/////////////////////////////

        void push_back(const T &x)
        {
            if (_finish >= _endOfStorage)
            {
                size_t old_capacity = capacity();
                size_t new_capacity = old_capacity == 0 ? 4 : 2 * old_capacity;
                reserve(new_capacity);
            }
            *_finish = x;
            _finish++;
        }

        void pop_back()
        {
            assert(size());
            _finish--;
        }

        void swap(vector<T> &v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endOfStorage, v._endOfStorage);
        }

        iterator insert(iterator pos, const T &x)
        {
            assert(pos >= _start && pos <= _finish);
            if (pos == _finish)
            {
                push_back(x);
                return pos;
            }
            if (_finish >= _endOfStorage)
            {
                size_t old_pos = pos - _start;

                size_t old_capacity = capacity();
                size_t new_capacity = old_capacity == 0 ? 4 : 2 * old_capacity;
                reserve(new_capacity);
                pos = _start + old_pos;
            }
            iterator tmp = _finish - 1;
            _finish++;
            while (tmp > pos)
            {
                *(tmp + 1) = *tmp;
                tmp--;
            }
            *tmp = x;
            return pos;
        }

        iterator erase(iterator pos)
        {
            assert(pos >= _start && pos < _finish);
            iterator tmp = pos;
            while (tmp < _finish)
            {
                *tmp = *(tmp + 1);
                tmp++;
            }
            _finish--;
            return pos;
        }
        void print()
        {
            for (auto e : *this)
            {
                std::cout << e << ' ';
            }
            std::cout << std::endl;
        }

    private:
        iterator _start = nullptr;

        iterator _finish = nullptr;

        iterator _endOfStorage = nullptr;
    };

}